{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.1 线性代数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 梗直哥串讲\n",
    "多数人大一大二线性代数都学得晕晕乎乎的，教材往往编的十分枯燥，假如没遇到好的老师，那基本上就是天书一般的存在。能学懂学透的同学是少数，否则你也根本没必要看这章内容。等到学机器学习时，基本上也忘的差不多了。虽然很多机器学习和深度学习的书都有数学回顾这部分内容，但遗憾的是往往也只是知识点堆砌，缺少顺理成章的解释。\n",
    "\n",
    "怎样才是好的面向深度学习的线性代数基础知识复习呢？一句话，就是要**讲清楚二者之间的关系，为什么要学线数，对深度学习的重要性在哪里，掌握哪些知识点就够了**？这将是咱们这章与众不同的地方。\n",
    "\n",
    "先问你个灵魂拷问：想过我们**为什么要学线性代数吗？** 实话说我也是花了好久才慢慢悟到一些答案的，因为真的没看到有什么书能把这个特别基本的问题讲的很清楚的。一句话描述，**它是用极简的数学语言表征我们面对的复杂世界的**。为啥这么说呢？数学家眼里的世界就是两种：**线性的和非线性的**。多数情况下，真实的系统都是非线性的，但表达和处理起来太复杂，因此习惯性的先把它线性化，然后用线性代数来表示。这么说你可能还是感觉有点枯燥，那咱们用一个图像处理的例子来说清楚。\n",
    "\n",
    "<img src=\"./images/2-1-1.png\" width=\"50%\" ></img>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "一个美女在你身旁，那感觉绝对是“非线性的”，各种脸红心跳的感觉，一言难尽是不是？可这样显然就缺少点儿数学思维了，计算机也根本处理不了。怎么记住这美妙的一刻呢？拍张照片呀!“咔嚓”的那一刻，就作了“线性化”。三维凸凹有致的身材，四维让人心动的眼神，五维醉人心脾的微笑，六维似曾相识的默契，七维....一下子都被压缩到了一张简单的二维平面照片中。剩下的故事就是线性代数能够表征的世界了，我们来看看。\n",
    "\n",
    "整张照片是由一个个的像素点组成，每个像素点可以用红绿蓝三基色来表示，每个通道都是一个0-255的灰度值。来看看如何用线性代数的语言来描述这件事。每个灰度值叫做标量(scalar)。每个像素点可以表示成RGB三个值，比如$[100,100,100]$，这叫向量。怎么表示很多个像素点呢？先单纯看其中一个通道，比如红色通道，画出来就是一幅灰度图像，用线性代数表示就是矩阵matrix。当然，我们也可以把所有的行zigzag这样“之”字形的连起来，表示为一个大的行向量或列向量。\n",
    "\n",
    "那怎么同时表示RGB三个通道呢？一个矩阵是不是就不够了？需要同时三个矩阵或者数组，这就是张量tensor的概念了。我们看到，从标量、向量、矩阵、张量表达的东西越来越复杂。一幅简简单单的照片图像中居然包含了这么多线性代数的基本概念。前面的三个都可以看成是张量的特例，标量因此也叫零阶张量，向量叫一阶张量，矩阵叫二阶张量。阶数越高的张量表示的是越复杂的空间世界。除了RGB这三个特征外，你可以再叠加更多特征来描述这个美女，比如：皮肤白皙度，五官纹理诱人度等等，这时候就需要更高阶的张量来表示了。\n",
    "\n",
    "从真实世界的美女，到小小照片中能够用线性代数表示的线性空间，这是人类科学史上的巨大成就。所有的美女，以及像美女一样的物体、系统等等一切，都可以表示成为一个张量，进而用简洁的字母来表示。**这就是“线性+代数”的含义了。有了它，不光数学表示变得极简，我们还能用计算机来处理，这就是为什么学习深度学习必须要学好线性代数的原因了**。\n",
    "\n",
    "如同中学讲完代数，还要学习数和数之间的运算一样，线性代数中的加减乘除也十分重要，这就是线性变换的概念。对刚才的美女图像例子而言，这意味着我们可以对图像进行各种操作。比如加加减减就是变色，乘法就是变形等等。\n",
    "\n",
    "好了，理解完毕，为了知识的完备，咱们还是少不了像其他人一样八股啰嗦啰嗦，再稍微全面的梳理一下学好深度学习需要掌握的线性代数知识点。不过，哥给你的建议是，一是可以**跳着读，找自己不熟的复习**；二是**从线性代数是用来线性化的表征世界的角度来重温下面复习的这些概念**，希望你会有完全不同的认识和理解。实在想不通，就想想咱们刚举的美女例子。\n",
    "\n",
    "**梗直哥建议：1. 以下内容为了全面比较多，可以重点看黑体字提示，是和大家以前常看的书籍，比如Hinton《深度学习》、吴恩达深度学习课程、以及李沐等人《动手学深度学习》比较大的区别。可能会比较个性化，但是可以帮助你快速理解，希望喜欢。2. 建议从代码使用入手，不太有必要死记数学公式，几乎所有运算在numpy和pytorch中都有现成的API可供调用。难点是深刻理解各个概念的几何和物理含义，否则你会特别混乱，实战中看别人的眼花缭乱，但自己不知道什么时候该用哪个。**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "线性代数作为一个非常重要的数学分支，主要研究向量空间和线性变换。在机器学习中，线性代数的概念和工具被广泛应用于各种任务，包括数据表示、特征工程、模型训练和优化等。掌握必要的线性代数基础概念几乎是学好机器学习和深度学习的必要条件。具体来说，理由如下：\n",
    "\n",
    "首先，线性代数在数据表示方面有着重要的作用。例如，在机器学习中，向量是一种非常常见的数据表示方式。向量可以表示各种数据，包括数值、文本和图像。在机器学习中，向量常常被用来表示输入数据和输出数据。\n",
    "\n",
    "其次，线性代数在特征工程方面也有着重要的作用。例如，在机器学习中，经常需要对数据进行预处理，以提取有用的特征。这些特征可以帮助机器学习模型更好地学习数据。线性代数中的概念和工具可以用来计算向量间的相似性、计算向量的投影和求解线性方程组等，这些操作都可以帮助我们在特征工程过程中提取有效的特征。\n",
    "\n",
    "此外，线性代数在模型训练和优化方面也有着重要的作用。在机器学习中，我们常常使用线性回归模型来预测连续型目标变量。线性回归模型的参数就是一个向量，包含了所有的权重和偏差。在训练过程中，需要不断调整这些参数，以使模型能够更好地预测目标变量。线性代数中的概念和工具可以用来计算向量的内积、计算矩阵的行列式和逆矩阵等，这些操作都可以帮助我们在训练过程中快速求解参数的最优解。\n",
    "\n",
    "举个例子，假设现在有一个二维数据集，希望使用线性回归模型来预测目标变量 y。我们可以使用线性代数中的概念和工具来训练这个模型。可以将数据集中的每一个样本表示为一个二维向量 $(x1, x2)$。使用向量的内积来计算模型的参数，具体来说，可以使用最小二乘法来求解下列线性方程组：\n",
    "$$\\arg\\min_w∑(y - (w_1 \\cdot x_1 + w_2 \\cdot x_2 + b))^2 $$\n",
    "\n",
    "其中，$w1$、$w2$和$b$分别是模型的参数，即权重和偏差。求解这个方程组后，就可以得到最优的参数值，从而使用线性回归模型来预测目标变量。\n",
    "\n",
    "**咱们快速复习一下线性代数中最重要的一些基本概念，着重看看学好深度学习需要掌握到什么程度。**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1.1 标量"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "标量也叫0D张量，一个标量就是一个数，它只有大小，没有方向。在生活中，标量可以体现在很多方面。例如，在进行体重测量时，可以使用标量来表示体重。例如，使用标量 70 来表示体重 70 公斤。\n",
    "\n",
    "在 PyTorch 中，标量可以使用标准 Python 数字类型（如 int 和 float）表示。\n",
    "下面是一个标量的示例，其中 x 是一个浮点数标量："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor(3.1400)\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "\n",
    "x = torch.tensor(3.14)\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意，标量的形状是一个空的大小元组 torch.Size([])，表示它不包含任何维度。\n",
    "标量在机器学习中可能用于表示单个预测值，损失值，学习率或其他单个数字。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1.2 向量及其运算"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "向量也叫1D张量。向量只有一个轴，沿着行的方向，或者沿着列的方向。例如，一个4维向量，沿着轴有4个元素。在机器学习中，向量通常用来表示数据。\n",
    "\n",
    "向量可以用来表示各种数据，包括数值、文本和图像。在机器学习中，向量常常被用来表示输入数据和输出数据。例如，在图像分类任务中，输入可以是一张图像的像素数据，而输出可以是图像的类别。\n",
    "\n",
    "向量还可以被用来表示机器学习模型的参数。例如，在线性回归模型中，参数就是一个向量，包含了所有的权重和偏差。这些参数在训练过程中被不断更新，以使模型能够更好地预测目标变量。\n",
    "向量还可以被用来表示各种向量空间模型，例如词嵌入模型。在词嵌入模型中，每个单词都会被表示为一个向量，这些向量可以用来表示单词之间的关系。例如，在一个词嵌入模型中，\"man\" 和 \"woman\" 两个单词的向量可能会很相似，因为它们都是人类的代词。\n",
    "\n",
    "另外，向量还可以被用来表示向量空间模型的相似性，例如余弦相似性。在机器学习中，余弦相似性是一种常用的度量两个向量之间相似程度的方法。它的值越接近 1，则两个向量越相似；值越接近 0，则两个向量越不相似。\n",
    "\n",
    "向量还可以被用来表示向量空间模型的线性变换。在机器学习中，可以通过矩阵乘法来对向量进行线性变换。这种变换可以用来调整向量的方向和大小，从而使模型能够更好地学习数据。\n",
    "总之，向量在机器学习中扮演着非常重要的角色，它们可以用来表示各种数据和模型参数，并且在计算中也起到了重要作用。\n",
    "\n",
    "向量的运算也是非常重要的。可以对两个向量进行加法和减法运算，也可以对一个向量进行数乘和数除运算。这些运算可以用来调整向量的大小和方向，从而辅助机器学习模型的训练和预测。\n",
    "\n",
    "此外，向量有许多性质，例如向量点积、向量叉积和向量的模长。这些性质可以帮助我们更好地理解向量的意义，并且在计算中也有着重要的作用。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 PyTorch 中，可以使用 torch.Tensor 类来创建向量。例如，下面是如何创建一个 2D 向量："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([1., 2.])\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "\n",
    "# 创建一个 2D 向量\n",
    "vector = torch.Tensor([1, 2])\n",
    "print(vector)  # 输出: tensor([1., 2.])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以下是向量的一些基本运算。\n",
    "\n",
    "**梗直哥提示：加减法和数乘都比较简单，咱们就不讲了。难点是乘法，比标量运算，也就是大家都熟悉的中学乘法一下子多了好几种不同的运算，经常容易混，我会在讲完后一起对比告诉你怎么好记。**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. 内积(Inner Product)：也称为点积、点乘，是两个向量的线性相关程度的度量。**在数学上，内积是两个向量的乘积，结果是一个标量**。内积可以用来衡量两个向量的相似度或差异性，也可以用来求解向量的投影、夹角等问题。通常情况下，点乘与内积是指同一个概念，即两个向量的点积。如果 $\\boldsymbol{a} = (a_1, a_2, \\dots, a_n)$ 和 $\\boldsymbol{b} = (b_1, b_2, \\dots, b_n)$ 是两个向量，那么它们的内积 $\\boldsymbol{a} \\cdot \\boldsymbol{b}$ 是一个数，其值为 $\\sum_{i=1}^{n} a_i \\cdot b_i$。在 PyTorch 中，可以使用 torch.dot() 函数来计算内积："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor(11.)\n"
     ]
    }
   ],
   "source": [
    "# 内积\n",
    "a = torch.Tensor([1, 2])\n",
    "b = torch.Tensor([3, 4])\n",
    "c = torch.dot(a, b)\n",
    "print(c)  # 输出: 11.0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2. 外积(Outer Product)：也称为叉积、向量积、外乘，是两个向量的线性相关程度的度量。**外积的结果是一个向量**。通常情况下，外积是指两个向量的矩阵乘积，而矩阵乘积的结果是一个向量。如果 $\\boldsymbol{a} = (a_1, a_2, \\dots, a_n)$ 和 $\\boldsymbol{b} = (b_1, b_2, \\dots, b_n)$ 是两个向量，那么它们的外积 $\\boldsymbol{a} \\times \\boldsymbol{b}$ 是一个新的向量，其值取决于 $\\boldsymbol{a}$ 和 $\\boldsymbol{b}$ 的方向和大小。在 PyTorch 中，可以使用 torch.cross() 函数来计算外积。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([-3,  6, -3])\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "\n",
    "# 定义两个向量\n",
    "a = torch.tensor([1, 2, 3])\n",
    "b = torch.tensor([4, 5, 6])\n",
    "\n",
    "# 计算外积\n",
    "c = torch.cross(a, b)\n",
    "\n",
    "print(c)  # 输出: tensor([-3, 6, -3])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意，在 PyTorch 中，外积运算只支持3维向量。如果传入的向量维度不是3维，则会抛出异常。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "3. 向量的模长：向量的模长，又称为向量的大小或向量的长度，表示向量在空间中的长度。对于二维向量 $\\boldsymbol{a} = (a_1, a_2)$，其模长为 $\\sqrt{a_1^2 + a_2^2}$，对于三维向量 $\\boldsymbol{a} = (a_1, a_2, a_3)$，其模长为 $\\sqrt{a_1^2 + a_2^2 + a_3^2}$。在 PyTorch 中，可以使用 torch.norm() 函数来计算向量的模长："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor(5.0990)\n"
     ]
    }
   ],
   "source": [
    "# 向量的模长\n",
    "a = torch.Tensor([1,3, 4])\n",
    "length = torch.norm(a)\n",
    "print(length)  # 输出: 5.099"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "4. 单位向量：是向量的模长为 1 的版本，它表示向量在空间中的方向，而不是大小。对于二维向量 $\\boldsymbol{a} = (a_1, a_2)$，其单位向量为 $\\frac{1}{\\sqrt{a_1^2 + a_2^2}} (a_1, a_2)$，对于三维向量 $\\boldsymbol{a} = (a_1, a_2, a_3)$，其单位向量为 $\\frac{1}{\\sqrt{a_1^2 + a_2^2 + a_3^2}} (a_1, a_2, a_3)$。在 PyTorch 中，可以使用向量的模长计算出向量的单位向量："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([0.2673, 0.5345, 0.8018])\n"
     ]
    }
   ],
   "source": [
    "# 创建一个向量\n",
    "vector = torch.Tensor([1, 2, 3])\n",
    "\n",
    "# 计算向量的模长\n",
    "length = torch.norm(vector)\n",
    "\n",
    "# 除以模长得到单位向量\n",
    "unit_vector = vector / length\n",
    "\n",
    "print(unit_vector)  # 输出：tensor([0.26726124, 0.53452248, 0.8017837])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面是向量的基本性质和在 PyTorch 中如何使用这些性质的简单示例。这些性质在深度学习中都很常用，例如在计算神经网络的梯度时，可以使用向量加法和数乘来计算损失函数的导数；在计算神经网络的输出时，可以使用内积来计算权重和输入的点积；在训练神经网络时，可以使用向量的模长来计算输出结果的大小，并使用向量的单位向量来调整权重的方向。\n",
    "\n",
    "**梗直哥提示：从几何意义角度理解这几个概念的区别比较简单。内积表示两个向量$a$,$b$之间的夹角关系，大于0表示二者方向基本相同，夹角在0到90度之间；等于0表示二者垂直，也叫正交；小于0表示夹角在90到180度之间。外积或者说叉积表示垂直于向量$a$，$b$构成的平面的法向量，从而构建一个3D坐标系。这在三维图像学中非常重要。模的概念很简单，就是向量长度。单位向量注意是方向。还有一种对应元素相乘的情况，英文叫element-wise multiplication，也被称为哈达玛积（Hadamard product），这个咱们下面讲矩阵时再说。**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.1.3 矩阵及其运算"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "矩阵也叫2D张量, 有两个轴，是一种二维数据结构。矩阵是由多个数字组成的表格。每个数字在矩阵中都有一个对应的行号和列号。例如，可以用二元组 (i,j) 来表示矩阵中第 i 行第 j 列的数字。来看看矩阵运算。加减法和数乘都比较简单，咱们就不说了。重点讲乘法。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在深度学习中，矩阵被广泛用于神经网络的计算中。例如，在进行卷积运算时，会使用矩阵乘法来实现。假设有两个矩阵$A$和$B$，它们的大小分别是$m \\times n$和$n \\times p$。则可以使用矩阵乘法来计算它们的乘积$C$，其大小为$m \\times p$。矩阵$C$的第$i$行第$j$列的元素$C_{i,j}$可以表示为：\n",
    "\n",
    "$$C_{i,j} = \\sum_{k=1}^{n} A_{i,k} \\times B_{k,j}$$\n",
    "\n",
    "即$C_{i,j}$是$A$的第$i$行和$B$的第$j$列的对应元素的乘积之和。\n",
    "\n",
    "在 PyTorch 中，可以使用 torch.tensor 类来创建矩阵。下面是一个简单的例子，展示了如何使用 PyTorch 创建矩阵并进行矩阵乘法运算："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[1, 2],\n",
      "        [3, 4]])\n",
      "tensor([[5, 6],\n",
      "        [7, 8]])\n",
      "tensor([[19, 22],\n",
      "        [43, 50]])\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "\n",
    "# 创建矩阵 A\n",
    "A = torch.tensor([[1, 2], [3, 4]])\n",
    "print(A)\n",
    "\n",
    "# 创建矩阵 B\n",
    "B = torch.tensor([[5, 6], [7, 8]])\n",
    "print(B)\n",
    "\n",
    "# 计算矩阵乘积 C = A * B\n",
    "C = torch.mm(A, B)\n",
    "print(C)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在深度学习中，矩阵也被用来表示神经网络中的参数。例如，在进行卷积神经网络的训练时，会使用矩阵来表示卷积核。在使用循环神经网络进行序列模型的训练时，也会使用矩阵来表示循环神经网络的权值。\n",
    "\n",
    "矩阵在深度学习中应用广泛，并且在计算机视觉、自然语言处理等领域也广泛使用。例如，在进行图像分类时，会使用矩阵来表示图像的像素数据；在进行文本分类时，会使用矩阵来表示文本中的词语。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**梗直哥提示：矩阵乘法是有顺序的，$AB$和$BA$完全不同，甚至都可能压根不能相乘，必须要先看矩阵的大小，这是与我们通常意义上乘法的最大区别，也是特别容易犯错的地方。比如$size(A)=m \\times n$，$size(B)=n \\times k$，那么$AB$就行，因为$m\\times n\\times n \\times k$，中间两个$n$门当户对一亲嘴亲没了。否则就不行，例如这种情况下$BA$就不行，因为此时$n \\times k \\times m \\times n$，$k$和$m$不相等，俩矩阵亲不了嘴！在深度学习中，我们会大量使用矩阵运算，因此要特别注意其大小形状。**\n",
    "\n",
    "**梗直哥提示：矩阵乘法和向量乘法类似，包括内积（点积），外积（叉积）和哈达玛积（元素相乘）。要特别主要它们之间的区别。这方面几何意义的理解，有不少知乎文章已经写的很好了，可以多读读加深认知。总的来讲，很多传统教材按照行列式方式讲解理解起来是比较困难的。从列向量线性组合的角度更容易理解矩阵的几何意义。比如空间“基”的概念，具体可以参考下面几篇高赞文章：**\n",
    "\n",
    "[矩阵乘法核心思想](https://zhuanlan.zhihu.com/p/327042762)\n",
    "\n",
    "[矩阵乘法的本质是什么？](https://www.zhihu.com/question/21351965)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "代码实现方面，在 PyTorch 中，可以使用以下函数来计算矩阵的内积、外积和哈徳玛积：\n",
    "\n",
    "内积（inner product）：使用 torch.matmul 函数。\n",
    "外积（outer product）：使用 torch.ger 函数。\n",
    "哈徳玛积（Hadamard product）：使用 torch.mul 函数。\n",
    "下面是使用这些函数的例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[19., 22.],\n",
      "        [43., 50.]])\n",
      "tensor([[3., 4.],\n",
      "        [6., 8.]])\n",
      "tensor([[45., 60.],\n",
      "        [77., 96.]])\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "\n",
    "# 内积\n",
    "A = torch.Tensor([[1, 2], [3, 4]])\n",
    "B = torch.Tensor([[5, 6], [7, 8]])\n",
    "inner = torch.matmul(A, B)  # 输出: tensor([[19., 22.],\n",
    "                            #          [43., 50.]])\n",
    "\n",
    "# 外积\n",
    "x = torch.Tensor([1, 2])\n",
    "y = torch.Tensor([3, 4])\n",
    "outer = torch.ger(x, y)  # 输出: tensor([[3., 4.],\n",
    "                         #          [6., 8.]])\n",
    "\n",
    "# 哈徳玛积\n",
    "C = torch.Tensor([[5, 6], [7, 8]])\n",
    "D = torch.Tensor([[9, 10], [11, 12]])\n",
    "hadamard = torch.mul(C, D)  # 输出: tensor([[45., 60.],\n",
    "                            #          [77., 96.]])\n",
    "\n",
    "print(inner)\n",
    "print(outer)\n",
    "print(hadamard)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意：内积是将两个矩阵按矩阵乘法的规则相乘得到的矩阵。外积是将两个向量每一位分别相乘得到的矩阵。哈徳玛积是将两个矩阵每一位分别相乘得到的矩阵。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在深度学习编程中，可以使用 NumPy 或 PyTorch 进行矩阵运算。这两者都是 Python 的科学计算库，都提供了很多用于矩阵运算的函数。NumPy 是一个用于科学计算的 Python 库，它提供了大量的数学函数和矩阵运算函数，可以方便地进行向量化运算，可以提高程序的运行速度。PyTorch 是一个为深度学习而设计的张量库，它提供了与 NumPy 类似的矩阵运算函数，同时还支持 GPU 加速，可以更快地计算矩阵运算。哪种库更适合用于深度学习编程，取决于你的需求和偏好。如果你只需要进行基本的矩阵运算，且不需要使用 GPU 加速，则 NumPy 可能是一个不错的选择。如果你需要进行大量的矩阵运算，或者希望利用 GPU 加速来提高运算速度，则 PyTorch 可能是一个更好的选择。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.1.4 张量"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "张量是多维数组的抽象概括。它可以看作是向量和矩阵的推广。张量可以是任意阶数。比如向量就是一阶张量，一行多列，或者一列多行；矩阵就是二阶张量，含有多行、多列；三阶张量，含有多行、多列，多页；以此类推...如果你对张量的概念理解起来有困难，可以看看这篇文章和视频，一目了然。\n",
    "[怎么通俗地理解张量？](https://www.zhihu.com/question/23720923)\n",
    "\n",
    "张量的基本性质包括：张量的维度，即张量的阶数、轴数。张量的形状，即每一维的大小。张量的数据类型，即张量中的数据的类型，比如float32、int64等。\n",
    "\n",
    "在深度学习中，张量是基本的计算单位。我们使用张量来表示输入数据、模型参数、模型的输出等。深度学习框架（如PyTorch）提供了多种张量操作，使我们能够高效地进行深度学习的计算。\n",
    "\n",
    "举个例子，使用PyTorch编写一个简单的深度学习模型，来分类MNIST手写数字图像。首先，我们需要定义模型的结构，并使用张量来表示模型的参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "\n",
    "class SimpleModel(torch.nn.Module):\n",
    "    def __init__(self, num_inputs, num_outputs):\n",
    "        super().__init__()\n",
    "        self.linear = torch.nn.Linear(num_inputs, num_outputs)\n",
    "        \n",
    "    def forward(self, inputs):\n",
    "        return self.linear(inputs)\n",
    "    \n",
    "model = SimpleModel(28 * 28, 10)  # 定义一个有784个输入特征和10个输出类别的模型\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "然后，我们可以使用张量来表示输入数据和标签，并使用张量操作来计算模型的输出:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor(2.4556, grad_fn=<NllLossBackward0>)\n"
     ]
    }
   ],
   "source": [
    "# 假设我们有一批大小为64的输入数据和标签\n",
    "batch_size = 64\n",
    "inputs = torch.randn(batch_size, 28 * 28)  # 随机生成一批输入数据\n",
    "labels = torch.randint(0, 10, (batch_size,))  # 随机生成对应的标签\n",
    "\n",
    "outputs = model(inputs)  # 计算模型的输出\n",
    "loss = torch.nn.functional.cross_entropy(outputs, labels)  # 计算损失\n",
    "\n",
    "print(loss)  # 输出损失的值\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在这个例子中，我们使用了多个张量来表示输入数据、标签、模型的参数以及模型的输出。我们还使用了张量操作（如model(inputs)和torch.nn.functional.cross_entropy）来计算模型的输出和损失。\n",
    "\n",
    "深度学习框架（如PyTorch）还提供了许多其他的张量操作，比如线性变换、卷积、池化等。我们可以使用这些操作来构建复杂的深度学习模型，并通过计算梯度来训练模型参数。张量在深度学习中起着重要的作用，它是深度学习计算的基本单位。使用张量，我们可以高效地进行深度学习的计算，并构建复杂的深度学习模型。\n",
    "\n",
    "此外，在深度学习中，张量还可以用于表示不同的数据类型，如图像、文本、音频等。比如，我们可以使用张量来表示一张彩色图像，其中每个元素表示图像的一个像素。我们也可以使用张量来表示一段文本，其中每个元素表示文本的一个词或字符。在处理不同类型的数据时，我们可能需要使用不同的操作来处理张量。比如，在处理图像数据时，我们可能会使用卷积操作来提取图像的特征；在处理文本数据时，我们可能会使用词嵌入操作来将文本转换为数值型表示。\n",
    "\n",
    "总之，张量是深度学习中用于表示各种数据类型的基本单位。使用张量，我们可以高效地处理各种类型的数据，并构建能够处理这些数据的深度学习模型。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Next 2-2 微积分](./2-2%20微积分.ipynb)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
